package net.gdface.utils;

import static net.gdface.utils.SimpleLog.logString;
import static net.gdface.utils.SimpleLog.stackTraceOf;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 全局日志输出<br>
 * 用于核心模块输出调试日志,
 * 通过调用{@link #setOutput(OutputString)}方法设置日志输出接口实例应用层可以实现在指定平台的日志输出
 * @author guyadong
 *
 */
public class CoreDebugLog {
	public static final StandardOutput DEFAULT_OUTPUT = new StandardOutput();
	private OutputString output;
	private boolean trace = true;
	public CoreDebugLog() {
	}
	/**
	 * 输出日志信息<br>
	 * example:
	 * <pre>
	 * log("name : {},age:{}","tom",23);
	 * </pre>
	 * @param format 格式字符串,采用"{}"或"%s"为占位符
	 * @param args 填充占位符的参数列表,如果数量小于占位符个数则多出的占位符填充"null"
	 */
	public void log(String format, Object ... args){
		if(null != output ){
			Thread currentThread = Thread.currentThread();
			StackTraceElement stackTrace = currentThread.getStackTrace()[output.stackLeve()];
			String message = String.format("[%s] (%s:%d) %s %s\n",
					currentThread.getName(),
					stackTrace.getFileName(),
					stackTrace.getLineNumber(),
					stackTrace.getMethodName(),
					logString(format,args));
			output.log(message);
		}
	}

	/**
	 * 当{@code needLog}为{@code true}时输出日志信息<br>
	 * @see #log(String, Object...)
	 */
	public void log(boolean needLog,String format, Object ... args){
		if(needLog && null != output ){
			Thread currentThread = Thread.currentThread();
			StackTraceElement stackTrace = currentThread.getStackTrace()[output.stackLeve()];
			String message = String.format("[%s] (%s:%d) %s %s\n",
					currentThread.getName(),
					stackTrace.getFileName(),
					stackTrace.getLineNumber(),
					stackTrace.getMethodName(),
					logString(format,args));
			output.log(message);
		}
	}
	/**
	 * 输出异常堆栈<br>
	 * @param trace 是否输出异常堆栈信息
	 * @param e 异常对象,为{@code null}忽略
	 */
	public void logTrace(boolean trace, Throwable e){
		if(trace && null != e && null != output ){
			Thread currentThread = Thread.currentThread();
			StackTraceElement stackTrace = currentThread.getStackTrace()[output.stackLeve()];
			String message = String.format("[%s] (%s:%d) %s %s\n",
					currentThread.getName(),
					stackTrace.getFileName(),
					stackTrace.getLineNumber(),
					stackTrace.getMethodName(),
					stackTraceOf(e));
			output.log(message);
		}
	}
	/**
	 * 输出异常堆栈<br>
	 * @param e 异常对象,为{@code null}忽略
	 */
	public void logTrace(Throwable e){
		if(this.trace && null != e && null != output ){
			Thread currentThread = Thread.currentThread();
			StackTraceElement stackTrace = currentThread.getStackTrace()[output.stackLeve()];
			String message = String.format("[%s] (%s:%d) %s %s\n",
					currentThread.getName(),
					stackTrace.getFileName(),
					stackTrace.getLineNumber(),
					stackTrace.getMethodName(),
					stackTraceOf(e));
			output.log(message);
		}
	}
	/**
	 * 设置输出接口实例
	 * @param output 为{@code null}中止日志输出
	 */
	public void setOutput(OutputString output) {
		this.output = output;
	}
	/**
	 * 设置标准控制台日志输出接口实例<br>
	 * 如果是android平台则使用默日志认标签({@link AndroidOutput#DEFAULT_TAG})设置android平台的控制台输出接口实例
	 * @see StandardOutput
	 * @see #enableAndroidLog(String)
	 */
	public void enableConsoleLog() {
		if(Platform.isAndroid()){
			enableAndroidLog(null);
		}else{
			this.output = new StandardOutput();	
		}
	}
	/**
	 * android平台设置输出控制台接口实例
	 * @param tag 日志输出标签名,为{@code null}使用默认值{@link AndroidOutput#DEFAULT_TAG}
	 * @see AndroidOutput
	 */
	public void enableAndroidLog(String tag) {
		this.output = new AndroidOutput(tag);
	}
	
	/**
	 * 设置{@link #logTrace(Throwable)}否则输出异常堆栈信息
	 * @param trace
	 */
	public void setTrace(boolean trace) {
		this.trace = trace;
	}

	/**
	 * 消息输出接口<br>
	 * 应用层通过实现此接口实现日志输出
	 * @see StandardOutput
	 * @see AndroidOutput
	 * @author guyadong
	 *
	 */
	public static interface OutputString{

		/**
		 * 日志信息输出
		 * @param message 待输出日志信息
		 */
		void log(String message);
		/**
		 * 返回输出行号类名所需要的堆栈级数
		 * @since 2.8.0
		 */
		int stackLeve();
	}
	/**
	 * 基于标准控制台输出的{@link OutputString}实现
	 * @author guyadong
	 *
	 */
	public static class StandardOutput implements OutputString{

		@Override
		public void log(String message) {
			System.out.println(null == message ? "null" : message);
		}
		@Override
		public int stackLeve() {
			return 2;
		}
	}
	/**
	 * android平台日志(android.util.Log)控制台输出的{@link OutputString}实现<br>
	 * 非android平台调用会抛出{@link RuntimeException}异常
	 * @author guyadong
	 *
	 */
	public static class AndroidOutput implements OutputString{
		/** 默认日志标签 */
		public static final String DEFAULT_TAG = "COREDEBUG";
		private final String tag;
		private final Method v;
		/**
		 * 构造方法
		 * @param tag 日志输出标签名,为{@code null}使用默认值{@link #DEFAULT_TAG}
		 */
		public AndroidOutput(String tag){
			try {
				// 反射方式获取 Log.v 方法
				this.tag = (null == tag) ? DEFAULT_TAG : tag;
				Class<?> logClass = Class.forName("android.util.Log");
				v = logClass.getMethod("v", String.class,String.class);
			} catch (ClassNotFoundException e) {
				throw new UnsupportedOperationException("UNSUPPORTED PLATFORM,android reqiured");
			} catch (Exception e) {
				throw new RuntimeException(e);
			} 
		}
		/**
		 * 使用默认日志标签的构造方法
		 */
		public AndroidOutput() {
			this(null);
		}

		@Override
		public void log(String message) {
			try {
				v.invoke(null, tag,message == null ? "null" : message);
			} catch (InvocationTargetException e) {
				throw new RuntimeException(e.getTargetException());
			}catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		@Override
		public int stackLeve() {
			return 3;
		}
	}
}
